package cz.apnetis.fitweb.server.jdo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;

import javax.jdo.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jdo.support.JdoDaoSupport;
import org.springframework.transaction.annotation.Transactional;

import com.google.appengine.api.datastore.Key;

import cz.apnetis.fitweb.server.GenericDAO;

public class GenericDAOImpl<T, PK extends Serializable>  extends JdoDaoSupport implements GenericDAO<T, PK> {
  /** Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging */
  protected final Logger log = LoggerFactory.getLogger(getClass());
  private final Class<T> persistentClass;

  /** Constructor that takes in a class for easy creation of DAO */
  public GenericDAOImpl(Class<T> persistentClass) {
    this.persistentClass = persistentClass;
  }

  @Override
  @Transactional(readOnly = true)
  public boolean exists(PK id) {
    T entity = getJdoTemplate().getObjectById(this.persistentClass, id);
    return entity != null;
  }

  @Override
  @Transactional(readOnly = true)
  public T get(PK id) {
    T entity = getJdoTemplate().getObjectById(this.persistentClass, id);

    if (entity == null) {
      log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found...");
      throw new ObjectRetrievalFailureException(this.persistentClass, id);
    }

    return entity;
  }

  @Override
  @Transactional(readOnly = true)
  public T getByKey(Key id) {
    T entity = getJdoTemplate().getObjectById(this.persistentClass, id);

    if (entity == null) {
      log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found...");
      throw new ObjectRetrievalFailureException(this.persistentClass, id);
    }

    return entity;
  }

  @Override
  @Transactional(readOnly = true)
  public List<T> getAll() {
   return new ArrayList<T>(getJdoTemplate().find(persistentClass));
  }

  @Override
  @Transactional(readOnly = true)
  public List<T> getAllDistinct() {
    Collection result = new LinkedHashSet(getAll());
    return new ArrayList(result);
  }

  @Override
  @SuppressWarnings("unchecked")
  public final List<T> executeQuery(final String queryName, final Object... params) {
    Query query = getPersistenceManager().newNamedQuery(persistentClass, queryName);

    List<T> result;
    if (params == null || params.length == 0) {
      result = (List<T>) query.execute();
    } else if (params.length == 1) {
      result = (List<T>) query.execute(params[0]);
    } else if (params.length == 2) {
      result = (List<T>) query.execute(params[0], params[1]);
    } else if (params.length == 3) {
      result = (List<T>) query.execute(params[0], params[1], params[2]);
    } else {
      result = (List<T>) query.execute(params);
    }
    return result;
  }

  @Override
  @Transactional(readOnly = false)
  public void remove(PK id) {
    getJdoTemplate().deletePersistent(this.get(id)); 
  }

  @Override
  @Transactional(readOnly = false)
  public T save(T object) {
    return getJdoTemplate().makePersistent(object);
  }
}